import contextlib
import pyaudio
import audioop
import numpy as np
import termcolor
import sys
import os

@contextlib.contextmanager
def ignore_stderr():
    devnull = os.open(os.devnull, os.O_WRONLY)
    old_stderr = os.dup(2)
    sys.stderr.flush()
    os.dup2(devnull, 2)
    os.close(devnull)
    try:
        yield
    finally:
        os.dup2(old_stderr, 2)
        os.close(old_stderr)

def iterate_microphone(sample_rate=16000, chunk_dt=0.08, rms_threshold=2000, active_threshold=0.5, print_db=True, interval=0.16, backlog=1):
    chunk_size = round(chunk_dt * sample_rate)
    # with ignore_stderr():
    pa = pyaudio.PyAudio()
    stream = pa.open(format=pyaudio.paInt16,
                     channels=1,
                     rate=sample_rate,
                     input=True,
                     frames_per_buffer=chunk_size,
                     )
    recording = False
    audio = b''
    start = 0
    activity = 0
    rms_peak = rms_threshold
    while True:
        chunk = stream.read(chunk_size)
        audio += chunk
        rms = audioop.rms(chunk, 2)
        if print_db:
            db = np.log10(max(1, rms) / 32768) * 10
            db_peak = np.log10(max(1, rms_peak) / 32768) * 10
            termcolor.cprint('{:5.1f}dB / {:5.1f}dB'.format(db, db_peak), end='\r', flush=True, color='light_yellow' if recording else 'light_grey', file=sys.stderr)
        if rms >= rms_peak and len(audio) >= sample_rate * 2:
            rms_peak = (rms - rms_peak) * np.exp(-sample_rate / chunk_size) + rms_peak
            activity = min(activity + 2 * chunk_size / sample_rate, 1)
        else:
            if rms_peak > rms_threshold:
                rms_peak = (rms_threshold - rms_peak) * np.exp(-sample_rate / chunk_size) + rms_peak
            activity = max(activity - chunk_size / sample_rate, 0)
        if not recording:
            audio = audio[-round(sample_rate * backlog) * 2:]
            if activity >= active_threshold:
                start = len(audio)
                recording = True
        if recording:
            if activity <= 0:
                yield audio, True
                recording = False
            else:
                now = len(audio)
                if now >= start + round(sample_rate * interval) * 2:
                    start = now
                    yield audio, False
